/* Copyright (C) 2012  Chris Mueller <ruunsmail@googlemail.com>
 *
 * This file is part of the Liquid Programming Language
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "nodes.h"
#include "array.h"
#include "map.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>



struct LqNode*                  
lq_node_new(int line, enum LqNodeKind kind, size_t children)
{
	struct LqNode* node = malloc(sizeof(struct LqNode));

	node->line = line;
	node->kind = kind;
	node->symbols = 0;

	if(children > 0)
		node->children = lq_ptr_array_new(children);
	else
		node->children = lq_ptr_array_new(1);

	return node;
}


void                    
lq_node_free(struct LqNode* node)
{
	assert(node != 0);
	
	switch(node->kind) {
		case LQ_NODE_INTEGER:
			mpz_clear(node->integer);
			break;
		case LQ_NODE_FLOATPOINT:
			mpf_clear(node->floatpoint);
			break;
		case LQ_NODE_STRING:
		case LQ_NODE_ATOM:
                case LQ_NODE_LET:
                case LQ_NODE_IDENTIFIER:
			free(node->string);
			break;
		default:
			break;
	}

	if(node->symbols != 0) 
		lq_map_free(node->symbols);

	lq_ptr_array_clear(node->children, lq_cast(lq_free_funptr, lq_node_free));

	lq_ptr_array_free(node->children);

	free(node);
}


void
lq_node_append(struct LqNode* parent, struct LqNode* child)
{
        assert(parent != 0);

        lq_ptr_array_append(parent->children, child);
}


size_t			
lq_node_children(struct LqNode* node)
{
        assert(node != 0);

        return lq_ptr_array_size(node->children);
}


struct LqNode*		
lq_node_index(struct LqNode* parent, size_t index)
{
        assert(parent != 0);

        return lq_ptr_array_get(parent->children, index);
}





byte*
lq_node_to_string(const struct LqNode* node)
{
	assert(node != 0);
	char buffer[24];
	byte* str = 0;

	switch(node->kind) {
		case LQ_NODE_ROOT: 		str = "root"; break;
		case LQ_NODE_PLUS:		str = "+"; break;
		case LQ_NODE_MINUS:		str = "-"; break;
		case LQ_NODE_MULTIPLY:  	str = "*"; break;
		case LQ_NODE_DIVIDE:		str = "/"; break;
		case LQ_NODE_MODULO:		str = "mod"; break;
		case LQ_NODE_POWER:		str = "^"; break;
		case LQ_NODE_BAND:		str = "&"; break;
		case LQ_NODE_BOR:		str = "|"; break;
		case LQ_NODE_BNOT:		str = "~"; break;
                case LQ_NODE_BXOR:              str = "><"; break;
		case LQ_NODE_BITSHIFTLEFT:	str = "<+"; break;
		case LQ_NODE_BITSHIFTRIGHT:	str = "+>"; break;
		case LQ_NODE_AND:		str = "and"; break;
		case LQ_NODE_OR:		str = "or"; break;
		case LQ_NODE_NOTEQUAL:		str = "<>"; break;
		case LQ_NODE_EQUAL:		str = "=="; break;
		case LQ_NODE_LESS:		str = "<"; break;
		case LQ_NODE_GREATER:		str = ">"; break;
		case LQ_NODE_LESSEQUAL:		str = "=<"; break;
		case LQ_NODE_GREATEREQUAL:	str = ">="; break;
		case LQ_NODE_MINUS_SIGN:	str = "-"; break;
                case LQ_NODE_SCOPING:           str = ":"; break;
		case LQ_NODE_CONCAT:		str = "++"; break;
		case LQ_NODE_REDUCE:		str = "--"; break;
		case LQ_NODE_CONS:		str = "::"; break;
		case LQ_NODE_ASSIGN:		str = "="; break;
		case LQ_NODE_BLOCK:		str = "block"; break;
		case LQ_NODE_INTEGER:
			return mpz_get_str(0, 10, node->integer);
		case LQ_NODE_CHARACTER:
			snprintf(buffer, sizeof(buffer), "%d (char)", node->character);
			str = buffer;
			break;
		case LQ_NODE_FLOATPOINT:
			return mpf_get_str(0, 0, 10, 3, node->floatpoint);
                case LQ_NODE_IDENTIFIER:        
		case LQ_NODE_ATOM:		
		case LQ_NODE_STRING:
		case LQ_NODE_MODULE:
                case LQ_NODE_LET:
			str = (byte*) node->string;
			break;
		case LQ_NODE_UNDERSCORE:	str ="_"; break;
		case LQ_NODE_IF:		str = "if"; break;
                case LQ_NODE_TUPLE:             str = "tuple"; break;
		case LQ_NODE_LIST:		str = "list"; break;
		case LQ_NODE_LIST_GENERATOR:	str = "<-"; break;
                case LQ_NODE_INDEX:             str = "!!"; break;
                case LQ_NODE_VALUE:             str = "value"; break;
		case LQ_NODE_FUN:		str = "fun"; break;
		case LQ_NODE_MATCH:		str = "match"; break;
		case LQ_NODE_BY:		str = "by"; break;
		case LQ_NODE_PATTERN:		str = "pattern"; break;
		default:
			str = "Unknown";
	}

	return lq_utf8_dup(str);
}


struct LqNode*                  
lq_node_integer_new(int line, byte* text, guint base)
{
	assert(text != 0);
	assert(base == 2 || base == 8 || base == 10 || base == 16);

	struct LqNode* node = lq_node_new(line, LQ_NODE_INTEGER, 0);

	mpz_init_set_str(node->integer, text, base);

	return node;
}


struct LqNode*                  
lq_node_floatpoint_new(int line, byte* text)
{
	assert(text != 0);

	struct LqNode* node = lq_node_new(line, LQ_NODE_FLOATPOINT, 0);

	mpf_init_set_str(node->floatpoint, text, 0);

	return node;
}


struct LqNode*                  
lq_node_character_new(int line, byte* text)
{
	assert(text != 0);

	struct LqNode* node = lq_node_new(line, LQ_NODE_CHARACTER, 0);

	node->character = g_utf8_get_char(text);

	return node;

}


struct LqNode*                  
lq_node_string_new(int line, byte* text, gboolean raw)
{
	assert(text != 0);

	struct LqNode* node = lq_node_new(line, LQ_NODE_STRING, 0);

	node->string = text;

	return node;
}

struct LqNode*          
lq_node_atom_new(int line, byte* text)
{
	assert(text != 0);

	struct LqNode* node = lq_node_new(line, LQ_NODE_ATOM, 0);
	node->string = text;

	return node;
}

	
struct LqNode*		
lq_node_identifier_new(int line, byte* text)
{
        assert(text != 0);

        struct LqNode* node = lq_node_new(line, LQ_NODE_IDENTIFIER, 0);

        node->string = text;

        return node;
}

struct LqNode*
lq_node_value_new(int line, byte* text)
{
        assert(text != 0);

        struct LqNode* node = lq_node_new(line, LQ_NODE_VALUE, 2);
        struct LqNode* identifier = lq_node_identifier_new(line, text);

        lq_node_append(node, identifier);

        return node;
}


struct LqNode*		
lq_node_module_new(int line, byte* text)
{
	assert(text != 0);

	struct LqNode* node = lq_node_new(line, LQ_NODE_MODULE, 4);

	node->string = text;

	return node;
}


struct LqNode*          
lq_node_let_new(int line, byte* text)
{
        assert(text != 0);

	struct LqNode* node = lq_node_new(line, LQ_NODE_LET, 4);

	node->string = text;

        return node;
}
